﻿using System;
using System.Reflection;
using System.Reflection.Emit;
using System.Collections;
using System.Collections.Generic;

namespace FeaturePlusInWeb
{
    public static class Help
    {
        public static object CreateDynamicXMLPropertyClass(Dictionary<string,string> objXMLNode)
        {
            object objReturn;

            //"Assembly text name, Version, Culture, PublicKeyToken","samplenamespace.sampleclass", 

            string strDynamicAssemblyName = "FeaturePlusInWeb";
            string strDynamicModuleName = "FeaturePlusInWeb.Model";
            string strDynamicClassName = "FeaturePlusInWeb.Model.FeaturedPlus" + DateTime.Now.Ticks.ToString();
            //string strDynamicClassName = "FeaturePlusInWeb.Model.FeaturedPlus";

            string strPropertyName = string.Empty;

            Type typeDynamic = null;
            FieldInfo fieldInfo = null;
            AppDomain currentDomain = null;
            TypeBuilder typeBuilder = null;
            ILGenerator ilGenerator = null;
            AssemblyName assemblyName = null;
            FieldBuilder fieldBuilder = null;
            ModuleBuilder moduleBuilder = null;
            MethodBuilder methodBuilder = null;
            PropertyBuilder propertyBuilder = null;
            AssemblyBuilder assemblyBuilder = null;
            MethodAttributes getSetAttr;
            Type[] methodArgs = { typeof(string) };

            currentDomain = AppDomain.CurrentDomain;
            assemblyName = new AssemblyName();
            assemblyName.Name = strDynamicAssemblyName;

            //Define a Dynamic Assembly
            assemblyBuilder = currentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
            //Define a Dynamic Module
            moduleBuilder = assemblyBuilder.DefineDynamicModule(strDynamicModuleName);
            //Define a runtime class with specified name and attributes.
            typeBuilder = moduleBuilder.DefineType(strDynamicClassName, TypeAttributes.Public);

            if (objXMLNode.Count > 0)
            {
                foreach(var objXMLAttribute in objXMLNode)
                {
                    strPropertyName = objXMLAttribute.Key;
                    // Add the class variable, such as "m_strIPAddress"
                    fieldBuilder = typeBuilder.DefineField("m_str" + strPropertyName, typeof(string), FieldAttributes.Public);
                    propertyBuilder = typeBuilder.DefineProperty(strPropertyName, System.Reflection.PropertyAttributes.HasDefault, typeof(string), null);
                    getSetAttr = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig;
                    methodBuilder = typeBuilder.DefineMethod("get_" + strPropertyName, getSetAttr, typeof(string), Type.EmptyTypes);
                    ilGenerator = methodBuilder.GetILGenerator();
                    ilGenerator.Emit(OpCodes.Ldarg_0);
                    ilGenerator.Emit(OpCodes.Ldfld, fieldBuilder);
                    ilGenerator.Emit(OpCodes.Ret);
                    propertyBuilder.SetGetMethod(methodBuilder);

                    methodBuilder = typeBuilder.DefineMethod("set_" + strPropertyName,
                       MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName,
                       typeof(void), methodArgs);
                    ilGenerator = methodBuilder.GetILGenerator();
                    ilGenerator.Emit(OpCodes.Ldarg_0);
                    ilGenerator.Emit(OpCodes.Ldarg_1);
                    ilGenerator.Emit(OpCodes.Stfld, fieldBuilder);
                    ilGenerator.Emit(OpCodes.Ret);
                    propertyBuilder.SetSetMethod(methodBuilder);
                }
            }

            // Add the class variable, such as "m_strIPAddress"
            strPropertyName = "XMLSource";
            fieldBuilder = typeBuilder.DefineField("m_str" + strPropertyName, typeof(string), FieldAttributes.Public);
            propertyBuilder = typeBuilder.DefineProperty(strPropertyName, System.Reflection.PropertyAttributes.HasDefault, typeof(string), null);
            getSetAttr = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig;
            methodBuilder = typeBuilder.DefineMethod("get_" + strPropertyName, getSetAttr, typeof(string), Type.EmptyTypes);
            ilGenerator = methodBuilder.GetILGenerator();
            ilGenerator.Emit(OpCodes.Ldarg_0);
            ilGenerator.Emit(OpCodes.Ldfld, fieldBuilder);
            ilGenerator.Emit(OpCodes.Ret);
            propertyBuilder.SetGetMethod(methodBuilder);

            //methodBuilder = typeBuilder.DefineMethod("set_" + strPropertyName,
            //   MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName,
            //   typeof(void), methodArgs);
            //ilGenerator = methodBuilder.GetILGenerator();
            //ilGenerator.Emit(OpCodes.Ldarg_0);
            //ilGenerator.Emit(OpCodes.Ldarg_1);
            //ilGenerator.Emit(OpCodes.Stfld, fieldBuilder);
            //ilGenerator.Emit(OpCodes.Ret);
            //propertyBuilder.SetSetMethod(methodBuilder);

            //Create Class 
            typeBuilder.CreateType();
            typeDynamic = moduleBuilder.GetType(strDynamicClassName);

            //Create Class Instance
            objReturn = Activator.CreateInstance(typeDynamic);

            if (objXMLNode.Count > 0)
            {
                foreach (var objXMLAttribute in objXMLNode)
                {
                    strPropertyName = objXMLAttribute.Key;
                    fieldInfo = typeDynamic.GetField("m_str" + strPropertyName);
                    fieldInfo.SetValue(objReturn, objXMLAttribute.Value);
                }
            }

            //// Keep the source of XML
            //strPropertyName = "XMLSource";
            //fieldInfo = typeDynamic.GetField("m_str" + strPropertyName);
            //fieldInfo.SetValue(objReturn, objXMLNode.ToString());

            return objReturn;
        }
    }
}
